package com.mapbased.sfw.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import org.dom4j.Attribute;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.Node;

import com.mapbased.sfw.binding.Comp;
import com.mapbased.sfw.binding.Req;
import com.mapbased.sfw.component.Component;
import com.mapbased.sfw.component.Components;
import com.mapbased.sfw.component.Headable;
import com.mapbased.sfw.component.StrRender;
import com.mapbased.sfw.component.Xmlable;

public class LoadContext {
	public static final String JWCID = "jid";

	private StringBuilder last = new StringBuilder(1024);
	private List<Renderable> renderables = new ArrayList<Renderable>();
	Map<String, Component> components = new java.util.HashMap<String, Component>();
	Set<Headable> headables = new java.util.HashSet<Headable>();
	public  AtomicInteger varIndex=new AtomicInteger();

	private LoadContext parent;

	public final Site site;

	Fragment loadingFragment;

	LoadContext(Site site) {
		this.site = site;
	}

	public static class PaserException extends java.lang.RuntimeException {
		public PaserException(String name) {
			super(name);
		}

	};

	public Map<String, Component> getComponents() {
		return this.components;
	}

	public Site getSite() {
		LoadContext lc = this;
		while (lc.site == null && lc.parent != null) {
			lc = lc.parent;
		}
		return lc.site;
	}

	public LoadContext createChild() {
		LoadContext lc = new LoadContext(this.site);
		lc.components = this.components;
		lc.headables = this.headables;
		lc.loadingFragment = this.loadingFragment;
		lc.varIndex=this.varIndex;
		lc.parent = this;
		
		return lc;
	}

	public int fetchIntAttribute(Element e, String name) {
		Attribute a = e.attribute(name);
		if (a != null) {
			String s = a.getValue();
			e.remove(a);
			return Integer.parseInt(s);
		}
		return 0;
	}

	public String fetchAttribute(Element e, String name) {
		Attribute a = e.attribute(name);
		if (a != null) {
			String s = a.getValue();
			e.remove(a);
			return s;
		}
		return null;
	}

	public RenderGroup getRenderGroup() {
		if (this.last.length() > 0) {
			renderables.add(new StrRender(last.toString()));
			last.setLength(0);
		}
		RenderGroup rg = new RenderGroup(this.renderables
				.toArray(new Renderable[this.renderables.size()]));
		return rg;
	}

	public void addString(String s) {
		last.append(s);
	}

	public void addRender(Renderable r) {
		if (last.length() > 0) {
			renderables.add(new StrRender(last.toString()));
			last.setLength(0);
		}
		this.renderables.add(r);
	}

	public Renderable parseComponent(Element ele, String jid) {
		Renderable r = Components.INSTANCE.create(jid,this);

		if (r == null) {
			throw new RuntimeException("Cannot create component:jid");
		}

		if (r instanceof Component) {
			Component com = (Component) r;
			String id = ele.attributeValue("id");
			if (id != null && !id.equalsIgnoreCase("")) {
				this.components.put(id, com);
			}
			com.setContainer(this.loadingFragment);
		}
		if (r instanceof Xmlable) {
			Xmlable x = (Xmlable) r;
			x.parseXml(ele, this);

		}
		if (r instanceof Headable) {
			this.headables.add((Headable) r);
		}
		return r;
		// String jwcid=ele.attributeValue(JWCID);
		// Component comp=null;
		// try
		// {
		// if(jwcid.trim().equalsIgnoreCase(""))
		// {
		// comp=new TemplateComponent();
		// }
		// else if(jwcid.indexOf(".") > 0)
		// {
		// Class c=loadClass(jwcid);
		// comp=(Component)c.newInstance();
		//
		// }
		// else
		// {
		//
		// Class cclass=(Class)componentClasses.get(jwcid.trim().
		// toLowerCase());
		//
		// comp=(Component)cclass.newInstance();
		// }
		// }
		// catch(Exception ex)
		// {
		// throw new java.lang.RuntimeException(
		// "Error creating component:<" +
		// ele.getName() + ">" + jwcid,ex);
		// }
		//
		// String a=ele.attributeValue("template");
		// String locale=getLocaleFromPath(lc.pagepath);
		// comp.locale=locale;
		// if(a != null)
		// {
		// String tplPath=a;
		// SAXReader reader=new SAXReader();
		// reader.setEntityResolver(WfwEntityResolver.INSTANCE);
		// Document doc=null;
		// try
		// {
		// URL u=this.localeResUrl(tplPath,locale);
		//
		// doc=reader.read(u);
		// org.dom4j.Attribute redir=
		// doc.getRootElement().attribute(Constants.AN_Redirect);
		// while(redir != null)
		// {
		// String s=redir.getValue().trim();
		// u=this.localeResUrl(s,locale);
		// if(u == null)
		// {
		// throw new java.lang.RuntimeException(
		// "Cannot find redirect template:" + s);
		// }
		// doc=reader.read(u);
		// redir=doc.getRootElement().attribute(Constants.
		// AN_Redirect);
		//
		// }
		//
		// }
		// catch(Exception ex1)
		// {
		// throw new java.lang.RuntimeException(
		// "Error loading template file for component :[" + a +
		// "]"
		// + jwcid,ex1);
		// }
		//
		// // this.resolveUrl(doc.getRootElement());
		//
		// Element root=(Element)doc.selectSingleNode(
		// "//*[@jwcid=\"$content$\"]");
		//
		// if(root != null)
		// {
		// root=(Element)root.elements().get(0);
		//
		// }
		// else
		// {
		//
		// root=doc.getRootElement();
		//
		// }
		//
		// //>>>>>>>>>>>>>>>>>build inhead string>>>>>>>>>>>>>>>>>>>>
		// List l=root.selectNodes("//*[@jwcid=\"tohead\"]");
		// String s="";
		// for(int i=0;i < l.size();i++)
		// {
		// Element n=(Element)l.get(i);
		// n.remove(n.attribute("jwcid"));
		// s+=n.asXML();
		// n.getParent().remove(n);
		//
		// }
		// ///////////////////////
		//
		// if(!s.equals(""))
		// {
		//
		// // //List segs=new java.util.Collections.();
		// // StringBufferBag ba=new StringBufferBag();
		// //
		// // //only parse config binding
		// // this.parseBinding(Util.removeSpace(s),
		// // java.util.Collections.EMPTY_LIST,ba);
		// //
		// // s=ba.buffer.toString();
		//
		// LoadContext nlc=new LoadContext(lc);
		// this.parseBinding(Util.removeSpace(s),nlc);
		//
		// comp.headSegments=nlc.getSegments();
		//
		// }
		//
		// //////////////////////////////
		// // comp.headString=s;
		// //<<<<<<<<<<<<build inhead string<<<<<<<<<<<<<<<<<<<<<
		// Element renderbody=(Element)doc.selectSingleNode(
		// "//*[@jwcid=\"body\"]");
		// if(renderbody != null)
		// {
		// /* for(java.util.Iterator i=ele.elementIterator();i.hasNext();)
		// {
		// Element e=(Element)i.next();
		// renderbody.add(e.detach());
		// }*/
		//
		// List es=ele.elements();
		// for(int i=0;i < es.size();i++)
		// {
		// Element e=(Element)es.get(i);
		// renderbody.add(e.detach());
		// }
		//
		// renderbody.remove(renderbody.attribute("jwcid"));
		//
		// }
		// //<<<<<<<<<<<<<<copy render body<<<<<<<<<<<<<<<<<<<<<<<<
		// //>>>>>>>>>>>>>>>copy
		// attributes>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		// for(int i=0;i < ele.attributeCount();i++)
		// {
		// Attribute aa=ele.attribute(i);
		// String an=aa.getName();
		// //skip template and jwcid attribute
		// if(an.equalsIgnoreCase(JWCID) ||
		// an.equalsIgnoreCase("template"))
		// {
		// continue;
		// }
		// //over ride same name attribute using new given value
		//
		// Attribute roota=root.attribute(an);
		// if(roota != null)
		// {
		// root.remove(roota);
		// }
		// root.add(aa.detach());
		// }
		// ele=root;
		//
		// }
		// else
		// {
		// ///////////////////////////////////
		// Node nnd=ele.attribute(JWCID);
		// if(nnd != null)
		// {
		// ele.remove(nnd); //.detach();
		// }
		// }
		// ele=comp.parseXml(ele,lc);
		//
		// //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		// if(ele != null)
		// {
		// LoadContext nlc=new LoadContext(lc);
		//
		// List l=nlc.segments;
		// StringBufferBag bag=nlc.last;
		//
		// parseElement(ele,nlc);
		// if(bag.buffer.length() > 0)
		// {
		// l.add(bag.getString());
		// }
		// comp.segments=l.toArray();
		// }
		// return comp;
	}

	public void parseNameSpace(Element ele) {
		Namespace nsp = ele.getNamespace();
		if (nsp != null) {
			if (ele.getParent() == null
					|| !nsp.getURI().equalsIgnoreCase(
							ele.getParent().getNamespace().getURI())) {
				String s = nsp.asXML();
				if (s.length() > 10) // make sure not add empty nsp
				{
					last.append(" ").append(s);
				}
			}
		}

	}

	public void parseElement(Element ele) {

		String a = this.fetchAttribute(ele, JWCID);// ele.attributeValue(JWCID);
		if (a != null) {
			Renderable r = this.parseComponent(ele, a);
			if (r != null) {
				this.addRender(r);
			}
			return;

		}

		this.last.append("<").append(ele.getName());
		this.parseAttributes(ele.attributes());
		this.parseNameSpace(ele);
		if (!ele.hasContent()) {
			String name = ele.getName();
			if ("input".equalsIgnoreCase(name) || "meta".equalsIgnoreCase(name)
					|| "link".equalsIgnoreCase(name)
					|| "img".equalsIgnoreCase(name)
					|| "br".equalsIgnoreCase(name)

			) {

				last.append(" />");

				return;
			}

		}

		last.append(">");

		// ////////////////////////////////////////////////////////////////////////
		parseElementChildren(ele);
		last.append("</").append(ele.getName()).append(">");

	}

	public void parseElementChildren(Element ele) {
		// List segments=lc.segments;

		Node n = null;
		boolean look = true;
		for (Iterator it = ele.nodeIterator();;) {
			if (look) {
				if (it.hasNext()) {
					n = (Node) it.next();

				} else {
					break;
				}
			}
			look = true;
			switch (n.getNodeType()) {
			case Node.ELEMENT_NODE:
				parseElement((Element) n);

				break;

			case Node.NAMESPACE_NODE:

			case Node.ATTRIBUTE_NODE:
				break;

			case Node.CDATA_SECTION_NODE:
				/**
				 * @todo:handle in future
				 */

			case Node.COMMENT_NODE:
				parseBinding(removeSpace(n.asXML()));
				break;

			case Node.PROCESSING_INSTRUCTION_NODE:
				last.append(" ").append(n.asXML());
				break;

			/**
			 * here because dom4j devide some text component unexpected
			 */
			case Node.TEXT_NODE:

				// Text t=(Text)n;
				StringBuilder tsb = new StringBuilder(n.getText());
				while (it.hasNext()) {
					n = (Node) it.next();
					if (n.getNodeType() == Node.TEXT_NODE) {
						tsb.append(n.getText());
					} else {
						look = false;
						break;
					}
				}

				parseBinding(removeSpace(tsb.toString()));

				// parseBinding(n.getText(),lc);

				break;

			}
		}

	}

	public final static String removeSpace(String s) {
		if (s == null || s.equals("")) {
			return "";
		}
		if (s.trim().equals("")) {
			return " ";
		}

		String[] ss = s.split("\n");
		StringBuffer sb = new StringBuffer(s.length());
		for (int i = 0; i < ss.length; i++) {
			String ts = ss[i].trim();
			if (!ts.equals("")) {
				sb.append(ts);
				if (i < ss.length - 1) {
					sb.append("\n");
				}
				// else
				// {
				// sb.append(" ");
				// }
			}
		}

		return sb.toString();

	}

	public void parseAttributes(List attributes) {

		for (int i = 0; i < attributes.size(); i++) {
			Attribute an = (Attribute) attributes.get(i);
			String anmame = an.getName();
			// if(anmame.endsWith("-"))
			// {
			// if(last.buffer.length() > 0)
			// {
			// segments.add(last.getString());
			// }
			// this.parseSVADAttribute(an,segments,lc);
			// continue;
			// }
			// =(Attribute)n;
			last.append(" ");
			last.append(anmame);
			last.append("=\"");
			parseBinding(an.getValue());
			last.append('"');
		}
	}

	private static String parseCmd(String s) {
		int st = s.indexOf("{");
		int et = s.indexOf("}", st);
		if (st < 0 || et <= st) {
			throw new java.lang.RuntimeException("Error parse giving Cmd:" + s);
		}
		return s.substring(st + 1, et);

	}

	private static String removeBindPart(String s) {
		int i = s.indexOf("}");
		if (i <= 0) {
			throw new java.lang.RuntimeException("Cannot find Cmd end tag:" + s);
		}

		return s.substring(i + 1);
	}

	public void parseBinding(String s) {
		if (s == null) {
			return;
		}

		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if (c == '@') {
				String bs = s.substring(i + 1);

				if (bs.startsWith("req{")) {

					this.addRender(new Req(parseCmd(bs)));
					parseBinding(removeBindPart(bs));
					return;

				}
				if (bs.startsWith("comp{")) {
					String cmd = parseCmd(bs);
					this.addRender(new Comp(cmd, this));

					parseBinding(removeBindPart(bs));
					return;

				}

				// else if(bs.startsWith("cookie{"))
				// {
				// if(last.buffer.length() > 0)
				// {
				// segments.add(last.getString());
				// }
				// segments.add(new CookieBinding(parseCmd(bs)));
				// parseBinding(removeBindPart(bs),lc);
				// return;
				//
				// }
				// //it's different to other bindings,
				// //WHEN loading page,calculte the value
				// else if(bs.startsWith("config{"))
				// {
				// Config config=this.global.wfwConfig;
				// String tmps=String.valueOf(config.getValue(parseCmd(bs)));
				// if(tmps.indexOf('@') >= 0)
				// {
				// parseBinding(tmps,lc);
				// }
				// else
				// {
				// last.buffer.append(tmps);
				// }
				// parseBinding(removeBindPart(bs),lc);
				// return;
				//
				// }

				else {
					last.append(c);

				}

			} else {
				last.append(c);
			}
		}

	}

}
